/**
 * Subdivision of Dispacher
 * Handles traffic related messages
 */
package br.unb.core.dispatcher;

import java.net.SocketException;
import java.util.HashMap;

import br.unb.core.message.Message;
import br.unb.core.traffic.Traffic;
import br.unb.core.traffic.ReceiverController;
import br.unb.core.traffic.SenderController;
import br.unb.graphics.qos.QoSController;
import br.unb.utils.Resource;

/**
 *
 * @author spider
 */
public class TrafficDispatcher {

    /** Senders map, used by the sender machine */
    static final HashMap sendersMap = new HashMap();
    /** Receivers map, used by the receiving machine */
    static final HashMap receiversMap = new HashMap();
    /** Specifies if the qosController is used or not*/
    public static boolean useQos = false;
    /** QoS Controller */
    public static final QoSController qosController = QoSController.getInstance();

    static {
        if (useQos) {
            qosController.play();
        } else {
            qosController.stop();
        }
    }

    /************************* Sender machine ****************************/
    //case Message.PREPARE_TO_SEND:
    /**
     * (Executed on sender machine)
     * Spaws a worker thread to send traffic.
     * @param traffic
     */
    public Message prepareToSend(Traffic traffic) {
        Message m = stopSender(traffic.getName());//view m to debug
        try {
            sendersMap.put(
                    traffic.getName(),
                    new SenderController(traffic));
            Dispatcher.getInstance().printError(
                    Resource.getString("READY_TO_GENERATE_TRAFFIC")
                    + traffic.getName());
            return new Message(Message.Type.ACK,
                    Resource.getString("SENDER_READY")
                    + traffic.getName());

        } catch (SocketException e) {
            Dispatcher.getInstance().printError(
                    Resource.getString("COULD_NOT_OPEN_PORT_ON_THE_HOST_FOR_GENERATION_OFF_THE_TRAFFIC")
                    + traffic.getName());
            return new Message(Message.Type.ERROR,
                    Resource.getString("COULD_NOT_OPEN_PORT_ON_THE_SOURCE_HOST_FOR_GENERATION_OF_THE_TRAFFIC")
                    + traffic.getName());
        } catch (Exception e) {
            //TODO verify size of error before sending back to the controller
            Dispatcher.getInstance().printError(
                    Resource.getString("ERROR")
                    + traffic.getName()
                    + " "
                    + e.toString());
            return new Message(Message.Type.ERROR,
                    (String) (Resource.getString("ERROR_ON_CLIENT")
                    + traffic.getName()
                    + " "
                    + e.toString()));
        }
    }

    //case Message.START_SENDER:
    /**
     * (Executed on sender machine)
     * Looks for a traffic in the table, if it prepared to send,
     * wake up the thread and start sending.
     * @param traffic
     */
    public Message startSender(String trafficName) {
        SenderController s = (SenderController) sendersMap.get(trafficName);
        if (s != null) {
            s.start();
            return new Message(Message.Type.ACK,
                    Resource.getString("SENDER_STARTED")
                    + s.getTraffic().getName());
        } else {
            return new Message(Message.Type.ERROR,
                    Resource.getString("SENDER_NOT_FOUND")
                    + trafficName);
        }
    }

    //case Message.STOP_SENDER:
    /**
     * (Executed on sender machine)
     * Stop a sender traffic on the running machine
     * @param traffic
     */
    public Message stopSender(String trafficName) {
        SenderController s = (SenderController) sendersMap.get(trafficName);
        if (s != null) {
            s.stop();
            Runtime.getRuntime().gc();
            return new Message(Message.Type.ACK,
                    Resource.getString("SENDER_STOPPED")
                    + s.getTraffic().getName());
        } else {
            return new Message(Message.Type.ERROR,
                    Resource.getString("SENDER_NOT_FOUND")
                    + trafficName);
        }
    }

    //case Message.DELETE_SENDER:
    /**
     * (Executed on sender machine)
     * Kills the worker thread that listens to traffic
     * @param traffic
     */
    public Message deleteSender(String trafficName) {
        Message m = this.stopSender(trafficName);
        sendersMap.remove(trafficName);
        return m;
    }

    /************************* Receiver machine ****************************/
    //case Message.PREPARE_TO_RECEIVE:
    /**
     * (executed on Receiver machine)
     * Spaws a worker thread to receive traffic.
     * @param traffic
     */
    public Message prepareToReceive(Traffic traffic) {
        Message m = stopReceiver(traffic.getName());//view m for debug
        try {
            receiversMap.put(
                    traffic.getName(),
                    new ReceiverController(traffic));
            Dispatcher.getInstance().printError(
                    Resource.getString("RECEIVING_NEW_TRAFFIC")
                    + traffic.getName());

            return new Message(Message.Type.ACK,
                    Resource.getString("RECEIVER_READY")
                    + traffic.getName());

        } catch (SocketException se) {
            Dispatcher.getInstance().printError(
                    Resource.getString("RECEIVING_NEW_TRAFFIC_FAILED")
                    + traffic.getName()
                    + "\n "
                    + Resource.getString("DETAIL")
                    + se.getMessage());
            return new Message(Message.Type.ERROR,
                    Resource.getString("DESTINATION_HOST_FAILURE")
                    + traffic.getDstIP()
                    + Resource.getString("THERE_HAS_BEEN_THE_FOLLOWING_ERROR")
                    + traffic.getName()
                    + "\n "
                    + Resource.getString("DETAIL")
                    + se.getMessage());
        } catch (Exception e) {
            //TODO verify size of e.getMessage() before sending back to the controller
            Dispatcher.getInstance().printError(
                    Resource.getString("ERROR")
                    + traffic.getName()
                    + " "
                    + e.toString()
                    + " "
                    + e.getMessage());

            return new Message(Message.Type.ERROR,
                    (String) (Resource.getString("ERROR_ON_CLIENT")
                    + traffic.getName()
                    + " "
                    + e.toString())
                    + " "
                    + e.getMessage());
        }
    }

    //case Message.STOP_RECEIVER:
    /**
     * (Executed on receiver machine)
     * Stops receiving data and sends a message to the
     * sender machine to stop sending packets.
     * @param traffic
     */
    public Message stopReceiver(String trafficName) {
        ReceiverController r = (ReceiverController) receiversMap.get(trafficName);
        if (r != null) {
            r.stop();
            receiversMap.remove(trafficName);
            return new Message(Message.Type.ACK,
                    Resource.getString("RECEIVER_STOPPED")
                    + r.getTraffic().getName());
        } else {
            return new Message(Message.Type.ERROR,
                    Resource.getString("RECEIVER_NOT_FOUND")
                    + trafficName);
        }
    }

    //case Message.DELETE_RECEIVER:
    /**
     * (Executed on receiver machine)
     * Kills the worker thread that receives traffic.
     * @param traffic
     */
    public Message deleteReceiver(String trafficName) {
        Message m = this.stopReceiver(trafficName);
        receiversMap.remove(trafficName);
        return m;
    }
}
